(** This module provides an intermediate representation of the users code. 
 If we imagine ,that the HaXe AST represents the HaXe language semantics and the Jvm_bytecode/Jvm_io modules represent JVM 
 ( not necessarily Java ! ) semantics, then this module is supposed to be 'inbetween'. Another way to think about it is, that this module is the 
 place where HaXe semantics get translated into jvm semantics...
 @author Oliver Jan Krylow 2010 
 *)

open Type

(** Types for the jvm oprand stack, constant pool etc. 
 These data types are the primitives of the jvm bytecode language. 
 Boolean, Byte, Short, Char, Int, Float, Long, Double, Constant Strings and AdressReferences are the most primitive types
 and are ( most often ) directly put into the opcode sequence.
 Whereas the Reference type is used to reference Classes , Interfaces and Arrays, that are further specified in the constant pool.
 The difference between the Reference and AdressReference types is that a Reference points to a global shared 'place' ,called 'constant pool',
 and an AdressReference points into the local code array it is used in. 
 Although, ultimately, a class file is just a stream of bytes, and every type, value and opcode described in this module is then represented as
 one or more specific bytes, it is not very useful to think in bytes and offsets unless we are dealing with limitations of the class file format,
 that most often are caused by the type of byte chosen to represent something ( for ex. : a method cannot have more than 2^16 instructions, because 
 the opcodes are expecting operands of 16bit size. )*)
type t =
	| Jvm_boolean of int (** 1bit true or false *)(* NOTE: all types lower than int are implicitly promoted to int *)
	| Jvm_byte of int (** 8bit signed 2-comp  -> -2^7 to ( 2^7 ) - 1 *)
	| Jvm_short of int (** 16bit signed 2-comp  -> -2^15 to ( 2^15 ) -1 *)
	| Jvm_char of int (** 16bit unsigned unicode * -> 0 to 2^16 *)
	| Jvm_int of int32 (** 32bit signed 2-comp -> -2^31 to ( 2 ^31 ) -1 *)
	| Jvm_float of float (** 32bit single-precision IEEE 754 *)(* ignoring extended-exponent value set for now ... *)
	| Jvm_long of int64 (** 64bit signed 2-comp  -> -2^63 to ( 2^63 ) -1 *)
	| Jvm_double of float (** 64bit double-precision IEEE 754 *)(* ignoring extended-exponent value set for now ... *)
	| Jvm_string of string (** this type only applys to static constants and is stored as mod utf8 in cp *)
	| Jvm_reference of jvm_reference (** reference to class, interface or array or null in cp*)
	| Jvm_addressRef of int (** 16bit unsigned index reference into class file itself *)


and jvm_reference =
	| ClassRef of jvm_path
	| InterfaceRef of jvm_path
	| ArrayRef of t
	| Jvm_null

and jvm_path =
	path

and jvm_params =
	t list

(** This type is meant to provide a means to logically bundle some jvm      
 classes and/or interfaces, which some times is necessary to express     
 haXe types                                                              *)
type jvm_construct =
	jvm_type_type list

(** This might be an unfortunate name, but I currently do not know a better 
 word for the group, where classes, interfaces and the likes belong to   
 ...                                                                     *)
and jvm_type_type =
	| Jvm_class of jvm_class
	| Jvm_interface of jvm_interface
	| Jvm_enum of jvm_enum

(** This type is supposed to represent a data structure ,that is easily transformable to a class file 
 or set of class files, while bridging the gap, between haXe classes and jvm classes. *)
and jvm_class =
	{ (* if super is None, implicitly extends java.lang.Object *)
		jvm_c_path : jvm_path;
		jvm_c_param_types : ( string * t ) list;
		jvm_c_implemented : jvm_interface list;
		jvm_c_super : jvm_class option; 
		jvm_c_synthetic : bool;
		jvm_c_public : bool; 
		jvm_c_init : jvm_method option;
		jvm_c_cinit : jvm_method option; 
		jvm_c_properties : jvm_property list
	}
(** Fortunately, haXe interfaces can be mapped pretty straightforward to jvm interfaces (which are also seperate class files),
 but since haXe classes can be "implemented" as though they were mere interfaces, it is useful to have a restricting data structure
 for interfaces only, instead of one structure that combines classes and interfaces. *)
and jvm_interface =
	{ (* implicitly extends java.lang.Object *) 
		jvm_i_path : jvm_path;
		jvm_i_param_types : ( string * t ) list;
		jvm_i_implemented : jvm_interface list; 
		jvm_i_synthetic : bool;
		jvm_i_public : bool; 
		jvm_i_cinit : jvm_method option;
		jvm_i_properties : jvm_property list
	}
(** At this point it is still undecided how to implement haXe enums on a jvm, but in all likelyhood it will
 heavily depend on the haXe "switch" keyword conversion, and will be optimized for performance.  *)
and jvm_enum =
	{ (* extends java.lang.Enum? *) 
		jvm_e_path : jvm_path; 
		jvm_e_public : bool
	}
(** It is unclear as of now, if such a datastructure is necessary, but we keep it around to remind ourselves of its existence from time to time *)
(* TODO:think how could this be useful *)
and jvm_annotation = 
	{
		jvm_a_path : jvm_path
	}

(** A data field with optional getter and setter methods. for example " var myVar(default,default) " in haxe *)
and jvm_property =
	| Jvm_property of jvm_field * jvm_method option * jvm_method option
	| Jvm_method of jvm_method

(** in contrast to haXe, a jvm makes a clear distinction between a field and a method (function). it is our duty to decide when haXe properties 
 should be reduced to simple fields ... *)
and jvm_field =
	{ 
		jvm_f_name : string; (** name declared by haxe user for field , or some modification of it *)
		
		jvm_f_type : t; (** the type of the field as jvm type *)
		jvm_f_scope : jvm_scope; 
		jvm_f_static : bool; 
		jvm_f_enum : bool; (** is element of enum *) 
		jvm_f_synthetic : bool; (** is compiler generated, has no source in user code *)
		jvm_f_volatile : bool; (** cannot be cached TODO:explore usefulness for haxe *)
		jvm_f_transient : bool; (** not serializable '' *) 
		jvm_f_final : bool;	(** haxe does not have a concept of a final or constant field, but maybe we can optimize some fields for jvm *)
		jvm_f_annotations : jvm_annotation list (** like deprecated etc...  maybe some haXe specific type annotations to be able to reparse compiled source files.  see jvm_annotation *)
	}

(** A method is a function associated with a class. it can only be referenced, if the class is known, it is not first class and a "this" pointer is always passed implicitly
 on invocation , allowing to reference the enclosing class. *)
and jvm_method =
	{ 
		jvm_m_name : string; 
		jvm_m_type : t; (** return type *)
		jvm_m_scope : jvm_scope; (** method visibility. private, protected or public. *)
		jvm_m_static : bool; 
		jvm_m_synthetic : bool;
		(* jvm_m_bridge: bool; (* TODO:explore semantics of this flag *) *)
		jvm_m_native : bool; (** implementation of this method is provided to jvm as platform dependend "native" code *)
		jvm_m_strict : bool; (** floating point strictness TODO:explore semantics ... *)
		jvm_m_params : (string * t) list; (** formal parameters of this method *)
		jvm_m_annotations : jvm_annotation list; 
		jvm_m_code : intercodes option (** the methods actual code logic is stored here as a list of generalized jvm bytecode insctructions ( opcodes ). *)
	}

(** Jvm opcode families. 
 A generalized ( more abstract ) form of jvm opcodes *)
and generic_opcode =
	| NoOp
	| (* * Load and store * *)
	Load of t
	| Store of t
	| StoreConst of t
	| LoadConst of t
	| (* * Arithmetic and logic * *)
	Add of t * t
	| Sub of t * t
	| Div of t * t
	| Mul of t * t
	| Rem of t * t
	| Shift of t * t
	| Neg of t
	| And of t * t
	| Or of t * t
	| XOr of t * t
	| Comp of t * t
	| IfComp of t * t
	| If
	| (* * Type conversion * *)
	ToInt of t
	| ToFloat of t
	| ToLong of t
	| ToDouble of t
	| ToShort of t
	| ToChar of t
	| ToByte of t
	| (* Object creation and manipulation *)
	New of t
	| NewArray of t
	| ArrayLength
	| Get
	| Put
	| CheckCast
	| InstanceOf
	| (* * Operand stack management * *)
	Pop
	| Push
	| Swap
	| (* * Control transfer * *)
	Goto
	| Switch
	| (* * Method invocation and return * *)
	Return of t
	| Invoke
	| (* * Throwing exceptions * *)
	Throw of t
	| (* * Monitor-based concurrency * *)
	MonitorEnter
	| MoniterExit

and intercodes =
	generic_opcode list

and jvm_scope =
	| Jvm_public | Jvm_private | Jvm_protected

exception Still_Unimplemented

(** A utility function, that returns a jvm field with some standard properties already initialized.
 can be useful, if you want the compiler to create a new field from scratch, without having to type 
 all record fields every time. 
 @param name Name of jvm field. 
 @return jvm_field record with most likely values *)
let simple_field name =
	{
		jvm_f_name = name;
		jvm_f_type = Jvm_reference Jvm_null;
		jvm_f_scope = Jvm_protected;
		jvm_f_static = false;
		jvm_f_enum = false;
		jvm_f_synthetic = false;
		jvm_f_volatile = false;
		jvm_f_transient = false;
		jvm_f_final = false;
		jvm_f_annotations = []
	}

(** A utility function, that returns a jvm method with some standard properties already initialized.
 @param name Name of jvm method. 
 @return jvm_method record with most likely values *)
let simple_method name =
	{
		jvm_m_name = name;
		jvm_m_type = Jvm_reference Jvm_null;(* => returns void *)
		
		jvm_m_scope = Jvm_private;
		jvm_m_static = false;
		jvm_m_synthetic = false;
		(* jvm_m_bridge = false; *)
		
		jvm_m_native = false;
		jvm_m_strict = false;
		jvm_m_params = [];
		jvm_m_annotations = [];
		jvm_m_code = None
	}
(** A utility function, that returns a jvm interface with some standard properties already initialized.
 @param path Package name and interface name. Ex: "pack1.pack2.ScarFace" becomes "["pack1";"pack2"],"ScarFace""  
 @return jvm_interface record with most likely values *)
let simple_interface path =
	{
		jvm_i_path = path;
		jvm_i_param_types = [];
		jvm_i_implemented = [];
		jvm_i_synthetic = false;
		jvm_i_public = true;
		jvm_i_cinit = None;
		jvm_i_properties = []
	}
(** A utility function, that returns a jvm class with some standard properties already initialized.
 @param path Path of jvm class. 
 @return jvm_class record with most likely values *)
let simple_class path =
	{
		jvm_c_path = path;
		jvm_c_param_types = [];
		jvm_c_implemented = [];
		jvm_c_super = None;
		jvm_c_synthetic = false;
		jvm_c_public = true;
		jvm_c_init =
			Some { (simple_method "<init>") with jvm_m_scope = Jvm_public; };
		jvm_c_cinit = None;(* TODO:find out if a static class construstor is necessary? *)
		jvm_c_properties = []
	}
(** A utility function, that returns a jvm enum with some standard properties already initialized.
 @param path Path of jvm enum. 
 @return jvm_enum record with most likely values *)
let simple_enum path =
	{
		jvm_e_path = path;
		jvm_e_public = true
	}

(** This exception may be thrown, if a users property is scoped as "(never,never)".
 we feel, that a compiler should prevent a user from nonesense like this, but this problem 
 is still up for debate. *)
exception NeverSayNever___Twice

(** Type.texpr -> Jvm_type.intercode list 
 @param e todo
 @return todo
 @see 'type.ml' todo*)
let rec as_intercodes e = 
	let convert_expr = function
		| TConst const -> []	
		| TLocal str -> []
		| TEnumField (enum , str) -> []
		| TArray (ex1 , ex2) -> []
		| TBinop (bop , ex1 , ex2) -> []
		| TField (ex , str) -> []
		| TClosure (ex , str) -> []
		| TTypeExpr modtype -> []
		| TParenthesis ex -> []
		| TObjectDecl strex_list -> []
		| TArrayDecl ex_list -> []
		| TCall (ex , ex_list) -> []
		| TNew (clazz , params , ex_list) -> []
		| TUnop (unop ,unopflag , ex) -> []
		| TFunction func -> []
		| TVars var_list -> []
		| TBlock ex_list -> []
		| TFor (str , t , ex1 , ex2) -> []
		| TIf (ex1 , ex2 , ex_option) -> []
		| TWhile (ex1 ,ex2 ,whileflag) -> []
		| TSwitch (ex , exs_list , ex_option) -> []
		| TMatch (ex , enum_list ,elem_list, ex_option) -> []
		| TTry (ex , catch_list) -> []
		| TReturn ex_option -> []
		| TBreak -> []
		| TContinue -> []
		| TThrow ex -> []
		| TCast (ex , modtype_option) -> []	
	in []

(** Type.tclass_field -> Jvm_type.jvm_method 
 @param is_static a boolean flag to indicate if a static method is to be created.
 @param field todo
 @return todo
 @see 'type.ml' todo*)
and as_method is_static field =
	{
		jvm_m_name = field.cf_name;
		jvm_m_type = as_type field.cf_type;
		jvm_m_scope =
			(match field.cf_public with
					| true -> Jvm_public
					| false -> Jvm_protected);
		jvm_m_static = is_static;
		jvm_m_synthetic = false;
		jvm_m_native = false;
		jvm_m_strict = false;
		jvm_m_params =
			List.map (fun (str, t) -> (str, (as_type t))) field.cf_params;
		jvm_m_annotations = [];
		jvm_m_code =
			(match field.cf_expr with
					| None -> None
					| Some e -> Some (as_intercodes e));
	}

(** Transforms Type.tclass_field to Jvm_type.jvm_property 
 @param is_static a boolean flag to indicate if a static method is to be created.
 @param field todo
 @return todo
 @see 'type.ml' todo*)
and as_property is_static field =
	match ((field.cf_get), (field.cf_set)) with
		| (NormalAccess, MethodAccess false) -> (** normal method TODO:remember private in HaXe is protected on jvm *)
			Jvm_method (as_method is_static field)
		| (NormalAccess, NormalAccess) -> (** simple field *)
			raise Still_Unimplemented
		| (InlineAccess, NeverAccess) -> (** becomes private final field or method. We can decide between the two by inspecting the expression, which should be 
		 reliable, since inlined things need to be initialized in haxe *)
			raise Still_Unimplemented
		| (NeverAccess, NeverAccess) -> (** this doesnt make much sense, a non readable and non writable might as well be non existant, thats why we simply do not 
		 compile it *)
			raise NeverSayNever___Twice
		| (g, s) -> 
			raise Still_Unimplemented

(** Takes in Type.t and spits out Jvm_type.t 
 @param t todo
 @return todo
 @see 'type.ml' todo *)
and as_type =
	function
	| TMono t_option_ref -> (* TODO:learn semantics of TMono *)
		raise Still_Unimplemented
	| TEnum (enum, params) ->
		raise Still_Unimplemented
	| TInst (clazz, params) -> raise Still_Unimplemented
	| TType (def, params) -> raise Still_Unimplemented
	| TFun (sig_list, return_type) -> raise Still_Unimplemented
	| TAnon anon -> raise Still_Unimplemented
	| TDynamic t -> raise Still_Unimplemented
	| TLazy ref -> raise Still_Unimplemented

and as_params p = List.map as_type p

(** Type.tclass -> Jvm_type.jvm_construct list
 @param hxclazz todo
 @param debug Determines , if debugging information is to be injected into class files
 @return todo
 @see 'type.ml' todo *)
let rec as_class debug hx_class = 
	[Jvm_class (simple_class ([ "";"" ],"Dummy")) ]

(** Type.tenum -> Jvm_type.jvm_construct list
 @param hx_enum todo
 @param debug Determines , if debugging information is to be injected into class files
 @return todo
 @see 'type.ml' todo*)
and as_enum debug hx_enum = 
	[Jvm_enum (simple_enum (["";""],"Dummy"))]

(** Type.tclass -> Jvm_type.jvm_construct list
 @param hx_class This parameter needs to be a class representation of a HaXe class as described in
'tclass'. One could argue, that this specific dependency undermines the whole modulariziation process
of HaXeJvm as an external library. But for various other reasons ( maintainabilty, structure and org., easy of dev. ) ,
the library approach proves useful. Moreover, it is not unreasonable to think, that in the future, HaXeJvm's modules
could be generalized, and so usable to other projects. 
 @param debug Determines , if debugging information is to be injected into class files
 @return Although the functions signature suggests, that is could return a list of an arbritrary number
of 'jvm_constructs', it is hereby guaranteed, that it will return a list with at most one 'Jvm_interface'.
The reason to return 'jvm_consruct list' ,instead of just a 'Jvm_interface', is consistency with
methods 'as_enum' and 'as_class', thereby simplifying the code necessary in 'genjvm.ml' 
 @see 'type.ml' For more information on HaXe classes
 @see 'genjvm.ml' That is currently the main "user" of this function*)
and as_interface debug hxclazz = [ Jvm_interface
	{
		(simple_interface hxclazz.cl_path)
		with
		
		
		jvm_i_synthetic = not hxclazz.cl_interface;
		jvm_i_public = not hxclazz.cl_private;
		
		jvm_i_properties = []
	}]

(** jvm_type_type -> path 
 @param tt todo
 @return todo
 @see 'type.ml' todo*)
and get_path = 
	function
	| Jvm_class cl -> cl.jvm_c_path
	| Jvm_interface inter -> inter.jvm_i_path
	| Jvm_enum en -> en.jvm_e_path

(** jvm_property -> jvm_method option * jvm_method option 
 @param prop todo
 @return todo*)
let extract_methods = 
	function
	| Jvm_property (_, get_option, set_option) -> (get_option, set_option)
	| Jvm_method meth -> ((Some meth), None)

(** jvm_property -> jvm_field option 
 @param prop todo
 @return todo*)
let extract_field = 
	function | Jvm_property (field, _, _) -> Some field | Jvm_method _ -> None

(** TODO! Those special values are to be replaced by interfaces declared in  
 haXe (once the jvm backend works ^^) An in-code description of the      
 Dynamic interface used to implement haXe Dynamic type. Later the        
 implementation code needs to be injected ...                            *)
and special_dynamic = simple_interface ([ "haxe"; "lang" ], "IDynamic")

(** An in-code description of the Array_access interface used to implement  
 haXe array_access                                                       *)
and special_array_access =
	simple_interface ([ "haxe"; "lang" ], "IArrayAccess")

(** An in-code description of the haxe.jvm.Closure interface used to        
 simulate function types on jvm                                          *)
and special_closure = simple_interface ([ "haxe"; "lang" ], "IClosure")
